//////////////////////////////////////////ok
#include"stdafx.h"

#include "bochs.h"

void IA32_CPU::RETnear32_Iw(Ia32_Instruction_c *i)
{
	Bit32u return_EIP;
	Bit16u imm16 = i->Iw();
	pop_32(&return_EIP);
	branch_near32(return_EIP); 
	  
	if ( sregs[IA32_SEG_REG_SS].cache.u.segment.d_b)
		ESP += imm16;
	else
		SP  += imm16;
}

void IA32_CPU::RETnear32(Ia32_Instruction_c *i)
{
	Bit32u return_EIP;
	pop_32(&return_EIP);
	branch_near32(return_EIP);
}

void IA32_CPU::RETfar32_Iw(Ia32_Instruction_c *i)
{
	invalidate_prefetch_q();
	Bit16u imm16 = i->Iw();
    return_protected(i, imm16);
	return;
}

void IA32_CPU::RETfar32(Ia32_Instruction_c *i)
{
	invalidate_prefetch_q();
    return_protected(i, 0);
	return;
}

void IA32_CPU::CALL_Ad(Ia32_Instruction_c *i)
{
	Bit32u new_EIP = EIP + i->Id();
	if ( new_EIP >  sregs[IA32_SEG_REG_CS].cache.u.segment.limit_scaled ) 
	{
		exception(IA32_GP_EXCEPTION, 0, 0);
	}

	/* push 32 bit EA of next instruction */
	push_32(EIP);
	EIP = new_EIP;
}

void IA32_CPU::CALL32_Ap(Ia32_Instruction_c *i)
{
	Bit16u cs_raw;
	Bit32u disp32;
	invalidate_prefetch_q();
	disp32 = i->Id();
	cs_raw = i->Iw2();
	call_protected(i, cs_raw, disp32);
	return;
}

void IA32_CPU::CALL_Ed(Ia32_Instruction_c *i)
{
	Bit32u op1_32;

	if (i->modC0()) 
	{
		op1_32 = IA32_READ_32BIT_REG(i->rm());
	}
	else 
	{
		read_virtual_dword(i->seg(), IA32_RMAddr(i), &op1_32);
	}

	if (op1_32 >  sregs[IA32_SEG_REG_CS].cache.u.segment.limit_scaled)
	{
		exception(IA32_GP_EXCEPTION, 0, 0);
	}

	push_32(EIP);
	EIP = op1_32;
}

void IA32_CPU::CALL32_Ep(Ia32_Instruction_c *i)
{
	Bit16u cs_raw;
	Bit32u op1_32;
	invalidate_prefetch_q();
	/* op1_32 is a register or memory reference */
	if (i->modC0()) 
	{
		exception(IA32_UD_EXCEPTION, 0, 0);
	}

	/* pointer, segment address pair */
	read_virtual_dword(i->seg(), IA32_RMAddr(i), &op1_32);
	read_virtual_word(i->seg(), IA32_RMAddr(i)+4, &cs_raw);
    call_protected(i, cs_raw, op1_32);
	return;
}

void IA32_CPU::JMP_Jd(Ia32_Instruction_c *i)
{
	Bit32u new_EIP = EIP + (Bit32s) i->Id();
	branch_near32(new_EIP); 
}

void IA32_CPU::JCC_Jd(Ia32_Instruction_c *i)
{
	bx_bool condition;

	switch (i->b1() & 0x0f) 
	{
		case 0x00: /* JO */ condition = get_OF(); 	break;
		case 0x01: /* JNO */ condition = !get_OF(); break;
		case 0x02: /* JB */ condition = get_CF(); break;
		case 0x03: /* JNB */ condition = !get_CF(); break;
		case 0x04: /* JZ */ condition = get_ZF(); break;
		case 0x05: /* JNZ */ condition = !get_ZF(); break;
		case 0x06: /* JBE */ condition = get_CF() || get_ZF(); break;
		case 0x07: /* JNBE */ condition = !get_CF() && !get_ZF(); break;
		case 0x08: /* JS */ condition = get_SF(); break;
		case 0x09: /* JNS */ condition = !get_SF(); break;
		case 0x0A: /* JP */ condition = get_PF(); break;
		case 0x0B: /* JNP */ condition = !get_PF(); break;
		case 0x0C: /* JL */ condition = getB_SF() != getB_OF(); break;
		case 0x0D: /* JNL */ condition = getB_SF() == getB_OF(); break;
		case 0x0E: /* JLE */ condition = get_ZF() || (getB_SF() != getB_OF());
			break;
		case 0x0F: /* JNLE */ condition = (getB_SF() == getB_OF()) && !get_ZF();
			break;
		default:
			condition = 0; // For compiler...all targets should set condition.
			break;
		}

	if (condition) 
	{
		Bit32u new_EIP = EIP + (Bit32s) i->Id();
		branch_near32(new_EIP); 
	}

}

void IA32_CPU::JZ_Jd(Ia32_Instruction_c *i)
{
	if (get_ZF()) 
	{
		Bit32u new_EIP = EIP + (Bit32s) i->Id();
		branch_near32(new_EIP); 
	}
}

void IA32_CPU::JNZ_Jd(Ia32_Instruction_c *i)
{
	if (!get_ZF()) 
	{
		Bit32u new_EIP = EIP + (Bit32s) i->Id();
		branch_near32(new_EIP); 
	}
}

void IA32_CPU::JMP_Ap(Ia32_Instruction_c *i)
{
	Bit32u disp32;
	Bit16u cs_raw;

	invalidate_prefetch_q();
	if (i->os32L()) 
	{
		disp32 = i->Id();
	}
	else 
	{
		disp32 = i->Iw();
	}
	cs_raw = i->Iw2();

	jump_protected(i, cs_raw, disp32);
	return;
}

void IA32_CPU::JMP_Ed(Ia32_Instruction_c *i)
{
	Bit32u new_EIP;
	/* op1_32 is a register or memory reference */
	if (i->modC0()) 
	{
		new_EIP = IA32_READ_32BIT_REG(i->rm());
	}
	else 
	{
		/* pointer, segment address pair */
		read_virtual_dword(i->seg(), IA32_RMAddr(i), &new_EIP);
	}

	branch_near32(new_EIP);
}

  /* Far indirect jump */

void IA32_CPU::JMP32_Ep(Ia32_Instruction_c *i)
{
	Bit16u cs_raw;
	Bit32u op1_32;
	invalidate_prefetch_q();
	/* op1_32 is a register or memory reference */
	if (i->modC0()) 
	{
		exception(IA32_UD_EXCEPTION, 0, 0);
	}

	/* pointer, segment address pair */
	read_virtual_dword(i->seg(), IA32_RMAddr(i), &op1_32);
	read_virtual_word(i->seg(), IA32_RMAddr(i)+4, &cs_raw);
	jump_protected(i, cs_raw, op1_32);
	return;
}

void IA32_CPU::IRET32(Ia32_Instruction_c *i)
{
	invalidate_prefetch_q();
	iret_protected(i);
	return;
}
